#region Terms of Usage
/*
 * Copyright 2006 Sameera Perera
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
#endregion

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using needle.Services.soab.Data;

namespace needle.Services.soab.RequestQueue
{
    public class QueueManager : IEnumerable<ServiceRequest>
    {
        protected List<ServiceRequest> rqstQueue;
        protected ReaderWriterLock queueLock;

        /// <summary>
        /// Occurs when a <see cref="ServiceRequest"/> is either enqueued or dequeued.
        /// </summary>
        public event EventHandler<RequestQueueUpdatedEventArgs> RequestQueueUpdated;

        /// <summary>Obtain the next transferable <see cref="ServiceRequest"/> from the queue.</summary>
        /// <returns>The <see cref="ServiceRequest"/> with <see cref="ServiceRequest.CanExecute"/>
        /// returning true.</returns>
        public ServiceRequest GetNextTransferableRequest()
        {
            ServiceRequest retVal = null;

            queueLock.AcquireReaderLock( -1 );
            // Find and return the first transferable request. 
            foreach ( ServiceRequest rqst in rqstQueue )
                if ( rqst.Status == ServiceRequestStatus.Queued && rqst.CanExecute )
                {
                    retVal = rqst;
                    break;
                }
            queueLock.ReleaseReaderLock();

            return retVal;
        }

        /// <summary>Enques the given service request.</summary>
        /// <param name="rqst">Request to be enqueued.</param>
        /// <param name="svc">Context on which to operate on.</param>
        /// <returns><c>true</c> if the request was successfully enqueued. <c>false</c> if
        /// the command was already found in the queue.</returns>
        public virtual bool Enqueue( ServiceRequest rqst )
        {
            bool retVal = false;

            // Do not requeue duplicate commands
            if ( !rqstQueue.Contains( rqst ) )
            {
                queueLock.AcquireWriterLock( -1 );
                if ( !rqstQueue.Contains( rqst ) )
                {
                    rqst.Status = ServiceRequestStatus.Queued;
                    retVal      = true;

                    rqstQueue.Add( rqst );
                    Database.Instance.Save( rqstQueue );
                }
                queueLock.ReleaseWriterLock();
            }
            // Fire the event
            if ( retVal && this.RequestQueueUpdated != null )
                this.RequestQueueUpdated( this, new RequestQueueUpdatedEventArgs( rqst ) );

            return retVal;
        }

        /// <summary>
        /// Removes the specified <see cref="ServiceRequest"/> from the queue.
        /// </summary>
        /// <param name="rqst">The <see cref="ServiceRequest"/> to dequeue.</param>
        /// <returns><c>true</c> if the request was dequeued, <c>false</c> otherwise.</returns>
        public virtual bool Dequeue( ServiceRequest rqst )
        {
            queueLock.AcquireWriterLock( -1 );
            bool retVal = rqstQueue.Remove( rqst );
            Database.Instance.Delete( rqst );
            Database.Instance.Save( rqstQueue );
            queueLock.ReleaseWriterLock();

            // Fire the event
            if ( retVal && this.RequestQueueUpdated != null )
                this.RequestQueueUpdated( this, new RequestQueueUpdatedEventArgs( rqst ) );

            return retVal;
        }

        #region Singleton Implementation

        public static readonly QueueManager Instance = new QueueManager();

        protected QueueManager()
        {
            queueLock       = new ReaderWriterLock();
            this.rqstQueue  = (List<ServiceRequest>)Database.Instance.GetObject( typeof( List<ServiceRequest> ) );

            if ( rqstQueue == null )
            {
                rqstQueue = new List<ServiceRequest>();
                Database.Instance.Save( rqstQueue );
            }
        }

        #endregion

        #region IEnumerable<ServiceRequest> Members

        public IEnumerator<ServiceRequest> GetEnumerator()
        {
            return this.rqstQueue.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.rqstQueue.GetEnumerator();
        }

        #endregion
    }
}
